﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data.Common;
using System.Globalization;
using Visifire.Charts;
using Microsoft.Win32;
using ResourceBlender.Express.Properties;
using ResourceBlender.DAL;
using ResourceBlender.Core;

namespace ResourceBlender.Express {
    /// <summary>
    /// Shows the completion of resources used by an application on a graph.
    /// </summary>
    public partial class WinCompletionSummary : Window {
        /// <summary>
        /// Gets or sets the IDs of the applications for which to show a completion summary.
        /// </summary>
        /// <value>The application ids.</value>
        public string[] ApplicationIds { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to include languages which are not used by the applications.
        /// </summary>
        /// <value><c>true</c> if unused languages should be shown, otherwise <c>false</c>.</value>
        public bool IncludeNotInUse { get; set; }

        private Style styHeading;

        /// <summary>
        /// Initializes a new <see cref="WinCompletionSummary"/>.
        /// </summary>
        public WinCompletionSummary() {
            InitializeComponent();
            styHeading = FindResource("HeadingFont") as Style;
            IncludeNotInUse = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WinCompletionSummary"/> class.
        /// </summary>
        /// <param name="applicationIds">The IDs of the applications for which to show a completion summary.</param>
        /// <param name="includeNotInUse"><c>true</c> if unused languages should be shown, otherwise <c>false</c>.</param>
        public WinCompletionSummary(string[] applicationIds, bool includeNotInUse) 
            : this() {
            this.ApplicationIds = applicationIds;
            this.IncludeNotInUse = includeNotInUse;
            chkCombined.Visibility = applicationIds.Length > 1 ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// Loads the list of unique languages when the window loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            NavigationCommands.Refresh.Execute(null, this);
        }

        /// <summary>
        /// Refreshes the completion summary.
        /// </summary>
        private void RefreshSummaries(object sender, RoutedEventArgs e) {
            BackgroundWorker worker = new BackgroundWorker();
            bool combined = chkCombined.IsChecked == true;
            worker.DoWork += (s, dwe) => {
                dwe.Result = ResourceBlenderDAL.GetResourceCompletionSummary(ApplicationIds, IncludeNotInUse, combined);
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Error != null) {
                    if(rwe.Error is MultipleException) {
                        MultipleException ex = rwe.Error as MultipleException;
                        DialogBox.Show(this, ex, "Warning", DialogBox.DialogBoxIcon.Warning);
                    } else {
                        DialogBox.ShowAlert(this, rwe.Error.Message, "Error");
                    }
                } else if(rwe.Result is List<ApplicationSummary>) { // exception might not have been fatal
                    tbcSummaries.Items.Clear();
                    List<ApplicationSummary> summaries = rwe.Result as List<ApplicationSummary>;
                    if(summaries.Count == 0)
                        return;
                    Style invisibleListBox = FindResource("InvisibleListBox") as Style;
                    foreach(ApplicationSummary summary in summaries) {
                        ListBox lst = new ListBox();
                        lst.Style = invisibleListBox;
                        ScrollViewer.SetHorizontalScrollBarVisibility(lst, ScrollBarVisibility.Disabled);
                        ScrollViewer.SetCanContentScroll(lst, false);
                        lst.Items.Add(summary);
                        Chart chart = new Chart();
                        lst.Items.Add(chart);
                        chart.MinWidth = 350;
                        chart.MinHeight = 20 * summary.CompletionMap.Count +80;
                        chart.ScrollingEnabled = false;
                        chart.Watermark = false;
                        chart.AnimationEnabled = false;
                        Title title = new Title();
                        title.Text = "Translation Completion";
                        title.FontWeight = FontWeights.Bold;
                        chart.Titles.Add(title);
                        Axis xAxis = new Axis(),
                             yAxis = new Axis();
                        xAxis.Title = "Language";
                        xAxis.AxisType = AxisTypes.Secondary;
                        yAxis.Title = "Completion (%)";
                        yAxis.TitleFontWeight = FontWeights.Bold;
                        yAxis.AxisMinimum = 0;
                        yAxis.AxisMaximum = 100;
                        yAxis.AxisType = AxisTypes.Primary;
                        chart.AxesX.Add(xAxis);
                        chart.AxesY.Add(yAxis);
                        chart.PlotArea.IsHitTestVisible = false;
                        DataSeries series = new DataSeries();
                        series.RenderAs = RenderAs.Bar;
                        series.IsHitTestVisible = false;
                        summary.CompletionMap.Reverse();
                        foreach(LanguageCompletion language in summary.CompletionMap) {
                            DataPoint point = new DataPoint();
                            //point.MarkerSize = 50;
                            //point.MinHeight = 550;
                            //point.Width = 50;
                            //point.MinWidth = 50;
                            point.IsHitTestVisible = false;
                            int completionPercentage = (int)Math.Round((language.NumTranslations / (double)summary.TotalElements) * 100.0, 0);
                            if(completionPercentage > 100) {
                                completionPercentage = 100;
                            } else if(completionPercentage < 0) {
                                completionPercentage = 0;
                            }
                            point.YValue = completionPercentage;
                            point.ToolTipText = string.Format("{0} - {1}%\n{2}/{3} translations", language.Culture.EnglishName, completionPercentage, language.NumTranslations,summary.TotalElements);
                            point.LegendText = language.Culture.EnglishName;
                            point.AxisXLabel = language.Culture.EnglishName;
                            point.LabelText = "      ";
                            point.LabelStyle = LabelStyles.OutSide;
                            point.LabelEnabled = true;
                            if(language.Image != null) {
                                try {
                                    ImageBrush brush = new ImageBrush(new BitmapImage(new Uri("pack://application:,,/Resources/images/flags/" + language.Image + ".png", UriKind.RelativeOrAbsolute)));
                                    brush.Stretch = Stretch.None;
                                    brush.AlignmentX = AlignmentX.Right;
                                    brush.AlignmentY = AlignmentY.Center;
                                    point.LabelBackground = brush;
                                } catch { /* there are a few cultures which FamFamFam hasn't got round to doing flags for yet, likely means the flag doesn't exist */ }
                            }
                            series.DataPoints.Add(point);
                        }
                        chart.Series.Add(series);
                        chart.VerticalAlignment = VerticalAlignment.Stretch;
                        TabItem tab = new TabItem();
                        tbcSummaries.Items.Add(tab);
                        tab.Header = summary.DisplayName;
                        if(summaries.Count == 1) {
                            tab.Height = 0;
                        }
                        tab.Content = lst;
                    }
                    tbcSummaries.SelectedIndex = 0;
                }
            };
            SetStatus("Loading languages...", true);
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Shows a save dialog then exports the summary to either HTML or CSV.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void Export(object sender, ExecutedRoutedEventArgs e) {
            try {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.OverwritePrompt = true;
                dlg.CheckPathExists = true;
                dlg.Title = "File To Save";
                dlg.AddExtension = true;
                dlg.InitialDirectory = string.IsNullOrEmpty(Settings.Default.LastGenerationPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : Settings.Default.LastGenerationPath;
                string mode = (e.Parameter ?? string.Empty).ToString();
                if(mode == "csv") {
                    dlg.Filter = "CSV files (.csv)|*.csv|All files|*.*";
                    dlg.DefaultExt = ".csv";
                    dlg.FileName = string.Format("Summary-{0}.csv", DateTime.Now.ToString("yyyy-MM-dd HHmm"));
                } else if(mode == "html") {
                    dlg.Filter = "HTML files (.html)|*.html|All files|*.*";
                    dlg.DefaultExt = ".html";
                    dlg.FileName = string.Format("Summary-{0}.html", DateTime.Now.ToString("yyyy-MM-dd HHmm"));
                } else {
                    throw new ResourceBlenderException("Invalid export mode");
                }
                if(dlg.ShowDialog() == true) {
                    string fileName = dlg.FileName;
                    bool combined =chkCombined.IsChecked == true;
                    BackgroundWorker worker = new BackgroundWorker();
                    worker.DoWork += (s, dwe) => {
                        dwe.Result = ResourceBlenderDAL.GetResourceCompletionSummary(ApplicationIds, IncludeNotInUse, combined);
                    };
                    worker.RunWorkerCompleted += (s, rwe) => {
                        if(rwe.Error != null) {
                            if(rwe.Error is MultipleException) {
                                MultipleException ex = rwe.Error as MultipleException;
                                DialogBox.Show(this, ex, "Warning", DialogBox.DialogBoxIcon.Warning);
                            } else {
                                DialogBox.ShowAlert(this, rwe.Error.Message, "Error");
                            }
                        } else if(rwe.Result is List<ApplicationSummary>) { // exception might not have been fatal
                            List<ApplicationSummary> summaries = rwe.Result as List<ApplicationSummary>;
                            List<ApplicationSummary.SummaryColumn> extraColumns = new List<ApplicationSummary.SummaryColumn>();
                            foreach(var itm in lstColumns.Items) {
                                ApplicationSummary.SummaryColumn column = itm as ApplicationSummary.SummaryColumn;
                                extraColumns.Add(column);
                            }
                            try {
                                if(mode == "csv") {
                                    ResourceGenerator.ExportToSummaryCSV(fileName, summaries);
                                } else if(mode == "html") {
                                    HtmlResourceGenerator.ExportToSummaryHTML(fileName, summaries, extraColumns);
                                }
                            } catch(Exception exportEx) {
                                DialogBox.ShowAlert(this, string.Format("Error exporting: {0}", exportEx.Message), "Error");
                            }
                        }
                    };
                    worker.RunWorkerAsync();
                }
            } catch(Exception ex) {
                DialogBox.ShowAlert(this, string.Format("Error exporting: {0}", ex.Message), "Error");
            }
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            WPFUtils.OpenHelp("Viewing.a.Resource.Summary.html");
        }

        /// <summary>
        /// Sets the text on the status message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="showProgress"><c>true</c> if the progress bar should be shown, otherwise <c>false</c>.</param>
        private void SetStatus(string message, bool showProgress) {
            prgStatus.Visibility = showProgress ? Visibility.Visible : Visibility.Hidden;
            if(message == null) {
                lblStatus.Visibility = Visibility.Hidden;
            } else {
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Content = message;
            }
        }

        /// <summary>
        /// Determines whether a new column can be added.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanAddColumn(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = IsLoaded && txtHtmlContent.Text.Trim().Length > 0 && txtHtmlHeader.Text.Trim().Length > 0;
        }

        /// <summary>
        /// Adds a new column.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void AddColumn(object sender, ExecutedRoutedEventArgs e) {
            var newColumn = new ApplicationSummary.SummaryColumn() {
                Header = txtHtmlHeader.Text.Trim(),
                Content = txtHtmlContent.Text.Trim()
            };
            lstColumns.Items.Add(newColumn);
            txtHtmlContent.Text = string.Empty;
            txtHtmlHeader.Text = string.Empty;
        }

        /// <summary>
        /// Determines whether a column is selected in the list.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void IsColumnSelected(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = IsLoaded && lstColumns.SelectedItem is ApplicationSummary.SummaryColumn;
        }

        /// <summary>
        /// Deletes the selected column.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteColumn(object sender, ExecutedRoutedEventArgs e) {
            var column = lstColumns.SelectedItem;
            lstColumns.Items.Remove(column);
        }
    }
}
